#include "head.h"

int FunInitTcpClint( const char strServerIpAddr[], const int Serverport )
{
    int SockFp = socket( AF_INET, SOCK_STREAM, 0 );
    if ( -1 == SockFp )
    {
        perror( "fail socket" );
        return -1;
    }

    struct sockaddr_in ServerAddr;
    ServerAddr.sin_family = AF_INET; 
    ServerAddr.sin_port   = htons( Serverport );
    ServerAddr.sin_addr.s_addr   = inet_addr( strServerIpAddr );
    int Ret = connect( SockFp, (struct sockaddr *)&ServerAddr, sizeof(ServerAddr) );
    if ( -1 == Ret )
    {
        perror( "fail connect" );
        return -1;
    }

    return SockFp;

}

int FunSendHttpRequest( const int SockTcpServerFd, const char strHttpReq[] )
{
    ssize_t SizeRet = send( SockTcpServerFd, strHttpReq, strlen( strHttpReq ), 0 );
    if ( -1 == SizeRet )
    {
        perror( "fail send" );
        return -1;
    }

    return SizeRet;
}

int FunRecvHttpResponse( const int SockTcpServerFd, const char strFileName[] )
{
    int Counter;

    FILE *fpDstFile = fopen( strFileName, "w" );
    if ( NULL == fpDstFile)
    {
        perror("fail fopen");
        return -1;
    }

    char strBuff[4096] = {0};
    ssize_t sizeRecvRet = 0;
    size_t sizeRetFwrite = 0;
    while ( ( sizeRecvRet = recv( SockTcpServerFd, strBuff, sizeof(strBuff), 0 ) ) > 0 )
    {
        sizeRetFwrite = fwrite( strBuff, sizeRecvRet, 1, fpDstFile );
        if ( -1 == sizeRecvRet )
        {
            perror( "fail fwrite" );
            return -1;
        }
        fflush( fpDstFile );
        Counter += sizeRecvRet;
        memset( strBuff, 0, sizeof(strBuff) );
    }
    if( -1 == sizeRecvRet )
    {
        perror( "fail recv" );
        return -1;
    }

    return Counter;

}


#define STR_SERVER_IP_ADDR  "119.96.200.204"    // http://news.sohu.com
#define SERVER_PORT         80                  // http 端口
#define STR_DST_FILE_NAME   "01_test_recv.txt"

int main(int argc, char const *argv[])
{
    int SockServerTcpFd;
    SockServerTcpFd = FunInitTcpClint( STR_SERVER_IP_ADDR, SERVER_PORT );

    int Ret;
    char *strRequest =
        "GET / HTTP/1.1\r\n"
		"Host: news.sohu.com\r\n"
		"User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:100.0) Gecko/20100101 Firefox/100.0\r\n"
		"Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8\r\n"
		"Accept-Language: en-US,en;q=0.5\r\n"
		"Connection: close\r\n" 
		"\r\n"; 

    Ret = FunSendHttpRequest( SockServerTcpFd, strRequest );
    if ( -1 == Ret )
    {
        perror( "fail send" );
        return -1;
    }
    printf( "Total Send Request # %d bytes\n", Ret );

    Ret = FunRecvHttpResponse( SockServerTcpFd, STR_DST_FILE_NAME ); 
    if ( -1 == Ret )
    {
        perror( "fail Recv" );
        return -1;
    }
    printf( "Total Recv Response # %d bytes\n", Ret );
    
    close( SockServerTcpFd );

    return 0;
}

/*
程序功能
    从 news.sohu.com 首页通过 http 获取首页的 html 内容, 
    并将获取到的内容保存在文件中

涉及函数
    socket()
    connect()
    send()
    recv()
    close()

    fopen()
    fwrite()
    fclose()

程序流程
    与 news.sohu.com 服务器建立 TCP连接 
        建立 socket套接字                       // socket()
        初始化 socket地址结构体                  // struct sockaddr_in
        建立 TCP连接                           // connect()

    发送 HTTP请求 给服务器              
        初始化 HTTP请求报文 字符串               // "GET / HTTP1.1 /r/n ..."
        发送请求到 TCP连接                      // send()                             

    接收 HTTP响应报文 并写入文件保存
        建立用来保存 HTTP响应报文 的文件流        // FILE*  =  fopen() 
        循环接收服务器的 HTTP响应报文 并存入文件   // recv()   fwrite()  fflush()
        关闭文件流                             //  fclose()

    关闭 TCP连接

知识要点
    HTTP请求报文 的格式
    HTTP响应报文 的格式
    谢希仁<计算机网络>第8版 P280

    TCP连接  的原子操作
    HTTP连接 的原子操作
*/